package xslice

// ToMapSet 将切片转换为一个去重后的用Map表示的集合 map[T]struct{}
//   - 如切片为空，返回空Map集合
//   - 注意: 切片元素为可比较类型, 返回Map不保证元素排序
func ToMapSet[T comparable](s []T) map[T]struct{} {
	l := len(s)
	if l == 0 {
		return map[T]struct{}{}
	}
	set := make(map[T]struct{}, l)
	for _, v := range s {
		set[v] = struct{}{}
	}
	return set
}

// Difference 计算切片的相对差集( A - B ), 即所有属于 A 但不属于 B 的元素
//   - A 为空时, 返回空切片
//   - B 为空时, 返回唯一元素的A切片
//   - A 和 B 都不为空时,	返回所有仅属于 A 但不属于 B 的唯一元素切片
//   - 注意: A 和 B 的元素类型必须相同, 且为可比较类型, 返回切片不保证元素排序
func Difference[T comparable](A, B []T) []T {
	lenA, lenB := len(A), len(B)
	if lenA == 0 {
		return []T{}
	}
	if lenB == 0 {
		return Unique(A)
	}

	// 构建 切片B 的集合用于快速查找
	bSet := make(map[T]struct{}, lenB)
	for _, v := range B {
		bSet[v] = struct{}{}
	}
	seen := make(map[T]struct{}, lenA)
	result := make([]T, 0, lenA)
	for _, v := range A {
		if _, in := bSet[v]; !in {
			if _, ok := seen[v]; !ok {
				seen[v] = struct{}{}
				result = append(result, v)
			}
		}
	}
	return result
}

// DifferenceSymmetric 计算切片的对称差集( A Δ B ), 即所有只在 A 或只在 B 中出现的元素
//   - A和B 都为空时, 返回空切片
//   - A 为空时, 返回唯一元素的B切片
//   - B 为空时,返回唯一元素的A切片
//   - A 和 B 都不为空时, 返回所有只在 A 或 B 中出现的唯一元素切片
//   - 注意: A 和 B 的元素类型必须相同, 且为可比较类型, 返回切片不保证元素排序
func DifferenceSymmetric[T comparable](A, B []T) []T {
	lenA, lenB := len(A), len(B)
	if lenA == 0 && lenB == 0 {
		return []T{}
	}
	if lenA == 0 {
		return Unique(B)
	}
	if lenB == 0 {
		return Unique(A)
	}

	setA := make(map[T]struct{}, len(A))
	for _, v := range A {
		setA[v] = struct{}{}
	}

	setB := make(map[T]struct{}, len(B))
	for _, v := range B {
		setB[v] = struct{}{}
	}

	result := make([]T, 0)

	// 处理 setA 中不在 setB 的元素
	for k := range setA {
		if _, found := setB[k]; !found {
			result = append(result, k)
		} else {
			delete(setB, k) // 移除交集部分
		}
	}

	// 处理 setB 中剩余的元素（不在 setA 的部分）
	for k := range setB {
		result = append(result, k)
	}
	return result
}

// Intersection 计算切片的交集( A ∩ B ), 即 A 与 B 两个切片中都出现的元素
//   - A 和 B 有一个为空时, 返回空切片
//   - A 和 B 都不为空时, 返回A 与 B 两个切片中都存在的唯一元素切片
//   - 注意: A 和 B 的元素类型必须相同, 且为可比较类型, 返回切片不保证元素排序
func Intersection[T comparable](A, B []T) []T {
	lenA, lenB := len(A), len(B)
	if lenA == 0 || lenB == 0 {
		return []T{}
	}
	// 构建较小的集合用于查找
	var (
		smallSet map[T]struct{}
		larger   []T
		seen     map[T]struct{}
		result   []T
	)

	if lenA < lenB {
		smallSet = make(map[T]struct{}, lenA)
		for _, v := range A {
			smallSet[v] = struct{}{}
		}
		larger = B
		seen = make(map[T]struct{}, lenA)
		result = make([]T, 0, lenA)
	} else {
		smallSet = make(map[T]struct{}, lenB)
		for _, v := range B {
			smallSet[v] = struct{}{}
		}
		larger = A
		seen = make(map[T]struct{}, lenB)
		result = make([]T, 0, lenB)
	}
	for _, v := range larger {
		if _, ok := smallSet[v]; ok {
			if _, added := seen[v]; !added {
				seen[v] = struct{}{}
				result = append(result, v)
			}
		}
	}
	return result
}

// Union 计算切片的并集( A ∪ B ), 即 A 与 B 两个切片中所有不重复的元素
//   - A 和 B 都为空时, 返回空切片
//   - A 或 B 有一个为空时, 返回不为空那个的唯一元素切片
//   - A 和 B 都不为空时, 返回A 与 B 两个切片中所有不重复的元素切片
//   - 注意: A 和 B 的元素类型必须相同, 且为可比较类型, 返回切片不保证元素排序
func Union[T comparable](A, B []T) []T {
	lenA, lenB := len(A), len(B)
	if lenA == 0 && lenB == 0 {
		return []T{}
	}
	if lenA == 0 {
		return Unique(B)
	}
	if lenB == 0 {
		return Unique(A)
	}

	seen := make(map[T]struct{})
	result := make([]T, 0, lenA+lenB)

	for _, v := range A {
		if _, ok := seen[v]; !ok {
			seen[v] = struct{}{}
			result = append(result, v)
		}
	}

	for _, v := range B {
		if _, ok := seen[v]; !ok {
			seen[v] = struct{}{}
			result = append(result, v)
		}
	}

	return result
}
